﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace IrcBot
{
    public partial class IrcBot : Form
    {
        public static string SERVER = "jubii.dk.quakenet.org";
        private static int PORT = 6667;
        private static string USER = "USER CSharpBot 8 * :I'm a C# irc bot";
        private static string NICK = "iofj39jf";
        private static string CHANNEL = "#zfire";
        public static StreamWriter writer;
        private Thread listenerThread;
        int countTimer = 0;
        int pollRunningTime = 0;
        bool pollRunning = false;
        List<option> options = new List<option>();
        List<string> hasVoted = new List<string>();
        public IrcBot()
        {
            InitializeComponent();
            listenerThread = new Thread(new ThreadStart(this.Start));
        }
        public void Start()
        {
            ThreadSafe(delegate { richTextBox1.AppendText("Bot Started/restarted\n"); });
            NetworkStream stream;
            TcpClient irc;
            StreamReader reader;
            string inputLine;
            try
            {
                irc = new TcpClient(SERVER, PORT);
                ThreadSafe(delegate { richTextBox1.AppendText("Connected to " + SERVER+"\n"); });
                stream = irc.GetStream();
                reader = new StreamReader(stream);
                writer = new StreamWriter(stream);
                writer.WriteLine("NICK " + NICK);
                writer.Flush();
                writer.WriteLine(USER);
                writer.Flush();
                while (true)
                {
                    while ((inputLine = reader.ReadLine()) != null)
                    {
                        //Console.WriteLine("<-" + inputLine);
                        //ThreadSafe(delegate { richTextBox1.AppendText("<-" + inputLine+"\n"); });

                        // Split the lines sent from the server by spaces. This seems the easiest way to parse them.
                        string[] splitInput = inputLine.Split(new Char[] { ' ' });
                        if (splitInput[0] == "PING")
                        {
                            string PongReply = splitInput[1];
                            //Console.WriteLine("->PONG " + PongReply);
                            writer.WriteLine("PONG " + PongReply);
                            writer.Flush();
                            continue;
                        }

                        switch (splitInput[1])
                        {
                            // This is the 'raw' number, put out by the server. Its the first one
                            // so I figured it'd be the best time to send the join command.
                            // I don't know if this is standard practice or not.
                            case "001":
                                {
                                    writer.WriteLine("MODE " + NICK + " +x");
                                    writer.Flush();
                                    string JoinString = "JOIN " + CHANNEL;
                                    writer.WriteLine(JoinString);
                                    writer.Flush();
                                    ThreadSafe(delegate { richTextBox1.AppendText("Joined Channel: " + CHANNEL + "\n"); });
                                    break;
                                }
                            case "PRIVMSG":
                                {
                                    if (splitInput[3].Substring(0,2) != ":!")
                                    {
                                        break;
                                    }
                                    switch (splitInput[3].Substring(2).Split(' ')[0])
                                    {
                                        case "nick":
                                            {
                                                string nick = splitInput[0].Substring(1, splitInput[0].IndexOf("!") - 1);
                                                string message;
                                                if (splitInput[2].StartsWith("#"))
                                                {
                                                    message = "PRIVMSG " + CHANNEL + " :" + nick;
                                                }
                                                else
                                                {
                                                    message = "PRIVMSG " + nick + " :" + nick;
                                                }
                                                writer.WriteLine(message);
                                                writer.Flush();
                                                ThreadSafe(delegate { richTextBox1.AppendText(nick+" requested nick!\n"); });
                                                break;
                                            }
                                        case "poll":
                                            {
                                                if (pollRunning == true)
                                                {
                                                    writer.WriteLine("PRIVMSG " + CHANNEL + " :A poll is already running!");
                                                    writer.Flush();
                                                    break;
                                                }
                                                pollRunning = true;
                                                options.Clear();
                                                for (int i = 4; i < splitInput.Length; i++)
                                                {
                                                    string str = splitInput[i];
                                                    options.Add(option.ConvertFromString(str));
                                                }
                                                pollRunningTime = 180;
                                                ThreadSafe(delegate { timer1.Start(); });
                                                ThreadSafe(delegate { richTextBox1.AppendText("A poll has been started\n"); });
                                                break;
                                            }
                                        case "vote":
                                            {
                                                string nick = splitInput[0].Substring(1, splitInput[0].IndexOf("!") - 1);
                                                string vote = splitInput[4];
                                                if (pollRunning != true)
                                                {
                                                    writer.WriteLine("PRIVMSG " + CHANNEL + " :No poll is running!\n");
                                                    writer.Flush();
                                                    break;
                                                }
                                                if (options.FindIndex(delegate(option opt) { return opt.name == vote; }) != -1)
                                                {
                                                    if (!hasVoted.Contains(nick))
                                                    {
                                                        foreach (option opt in options)
                                                        {
                                                            if (opt.name == vote)
                                                            {
                                                                opt.votes++;
                                                                writer.WriteLine("PRIVMSG " + CHANNEL + " :Someone voted on " + opt.name);
                                                                writer.Flush();
                                                            }
                                                        }
                                                    }
                                                    else
                                                    {
                                                        writer.WriteLine("PRIVMSG " + nick + " :You have already voted on this poll!");
                                                        writer.Flush();
                                                    }
                                                }
                                                break;
                                            }
                                        default:
                                            {
                                                writer.WriteLine("PRIVMSG " + CHANNEL + " :UNRECOGNIZED COMMAND!");
                                                writer.Flush();
                                                ThreadSafe(delegate { richTextBox1.AppendText("UNRECOGNIZED COMMAND!\n"); });
                                                break;
                                            }

                                    }
                                    break;
                                }
                            default:
                                break;
                        }
                    }
                    // Close all streams
                    writer.Close();
                    reader.Close();
                    irc.Close();
                }
                
            }
            catch (Exception e)
            {
                ThreadSafe(delegate { richTextBox1.AppendText("\n" + e.ToString()); });
                Thread.Sleep(5000);
                Start();
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            listenerThread.Start();
        }

        protected virtual void ThreadSafe(MethodInvoker method)
        {
            if (InvokeRequired)
            {
                BeginInvoke(method);
            }
            else
            {
                method();
            }
        }
        private void tick()
        {
            if (countTimer == pollRunningTime)
            {
                pollRunning = false;
                ThreadSafe(delegate { timer1.Stop(); });
                string winner = "";
                int votes = 0;
                bool tie = false;
                foreach (option opt in options)
                {
                    if (opt.votes > votes)
                    {
                        winner = opt.name;
                        votes = opt.votes;
                        tie = false;
                    }
                    else if (opt.votes == votes)
                    {
                        tie = true;
                    }
                }
                if (tie == false)
                {
                    writer.WriteLine("PRIVMSG " + CHANNEL + " :The winner of the poll is " + winner + " with " + votes.ToString() + " votes!");
                    writer.Flush();
                }
                else
                {
                    writer.WriteLine("PRIVMSG " + CHANNEL + " :The poll is a tie!");
                    writer.Flush();
                }
                return;
            }
            int timeremaining = pollRunningTime-countTimer;
            if (timeremaining%30 == 0)
            {
                writer.WriteLine("PRIVMSG " + CHANNEL + " :Time remaining on the poll: "+timeremaining.ToString());
                writer.Flush();
            }
            countTimer++;
        }
        private class option
        {
            public string name { get; set; }
            public int votes { get; set; }
            public option(string str)
            {
                name = str;
                votes = 0;
            }
            public static List<option> ConvertFromArray(string[] array)
            {
                List<option> templist = new List<option>();
                foreach (string str in array)
                {
                    templist.Add(new option(str));
                }
                return templist;
            }
            public static option ConvertFromString(string str)
            {
                return new option(str);
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            tick();
        }
    }
}
